package asruby.yarv
{
import asruby.Ruby;
import asruby.lang.RubyArray;
import asruby.parser.LocalStaticScope;
import asruby.parser.StaticScope;
import asruby.runtime.IRubyObject;
import asruby.runtime.ThreadContext;

import flash.utils.Dictionary;
	
public class YARVRunner {
	
    private var runtime: Ruby;
    
    private var ym: YARVMachine = YARVMachine.INSTANCE;

    private var iseq: InstructionSequence;

    private var jumps: Dictionary = new Dictionary();
    private var labels: Dictionary = new Dictionary();

/*    	public function  YARVRunner(runtime:Ruby, input: Loader, filename: String) {
        this.runtime = runtime;
        var first: Array = new Array();
        try {
            input.read(first);
            if(first[0] != 'R' || first[1] != 'B' || first[2] != 'C' || first[3] != 'M') {
                throw new RuntimeException("File is not a compiled YARV file");
            }
            var f: RubyFile = new RubyFile(runtime,filename,input);
            var arr: IRubyObject = runtime.getMarshal().callMethod(runtime.getCurrentContext(),"load",f);
            iseq = transformIntoSequence(arr);
        } catch(e:Error) {
            throw new RuntimeException("Couldn't read from source",e);
        }
    }
     */
    public function  run(): IRubyObject {
        var context: ThreadContext = runtime.getCurrentContext();
        var scope: StaticScope = new LocalStaticScope(null);
        scope.setVariables(iseq.locals);
        context.setFile(iseq.filename);
        context.setLine(-1);
        return ym.exec(context, scope, iseq.body);
    }

    private function  transformIntoSequence(arr: IRubyObject): InstructionSequence {
        if(!(arr instanceof RubyArray)) {
            throw new Error("Error when reading compiled YARV file");
        }
        labels.clear();
        jumps.clear();

        var seq: InstructionSequence = new InstructionSequence(runtime,null,null,null);
/*         Iterator internal = (((RubyArray)arr).getArray()).iterator();
        var seq.magic:  = internal.next().toString();
        var seq.major:  = RubyNumeric.fix2int((IRubyObject)internal.next());
        var seq.minor:  = RubyNumeric.fix2int((IRubyObject)internal.next());
        var seq.format_type:  = RubyNumeric.fix2int((IRubyObject)internal.next());
        IRubyObject misc = (IRubyObject)internal.next();
        if(misc.isNil()) {
            var seq.misc:  = null;
        } else {
            var seq.misc:  = misc;
        }
        var seq.name:  = internal.next().toString();
        var seq.filename:  = internal.next().toString();
        var seq.line:  = new Object[0]; internal.next();
        var seq.type:  = internal.next().toString();
        var seq.locals:  = toStringArray((IRubyObject)internal.next());
        IRubyObject argo = (IRubyObject)internal.next();
        if(argo instanceof RubyArray) {
            Array arglist = ((RubyArray)argo).getArray();
            var seq.args_argc:  = RubyNumeric.fix2int((IRubyObject)arglist.get(0));
            var seq.args_arg_opts:  = RubyNumeric.fix2int((IRubyObject)arglist.get(1));
            var seq.args_opt_labels:  = toStringArray((IRubyObject)arglist.get(2));
            var seq.args_rest:  = RubyNumeric.fix2int((IRubyObject)arglist.get(3));
            var seq.args_block:  = RubyNumeric.fix2int((IRubyObject)arglist.get(4));
        } else {
            var seq.args_argc:  = RubyNumeric.fix2int(argo);
        } 

        var seq.exception:  = getExceptionInformation((IRubyObject)internal.next());

        Array bodyl = ((RubyArray)internal.next()).getArray();
        var body: YARVMachine.Instruction[] = new YARVMachine.Instruction[bodyl.size()];
        var real: int=0;
        var i: int=0;
        for(var iter: Iterator = bodyl.iterator();iter.hasNext();i++) {
            IRubyObject is = (IRubyObject)iter.next();
            if(is instanceof RubyArray) {
                body[real] = intoInstruction((RubyArray)is,real,seq);
                real++;
            } else if(is instanceof RubySymbol) {
                labels.put(is.toString(), new Integer(real+1));
            }
        }
        var nbody: YARVMachine.Instruction[] = new YARVMachine.Instruction[real];
        System.arraycopy(body,0,nbody,0,real);
        var seq.body:  = nbody;

        for(var iter: Iterator = jumps.keySet().iterator();iter.hasNext();) {
            YARVMachine.Instruction k = (YARVMachine.Instruction)iter.next();
            k.l_op0 = ((Integer)labels.get(jumps.get(k))).intValue() - 1;
        } */

        return seq;
    }

    private function  toStringArray(obj: IRubyObject): Array {
        if(obj.isNil()) {
            return new String[0];
        } else {
            var l:Array = obj.getArray();
            var s:Array = new Array();
            var i: int=0;
            for(var iter: Object in l) {
                s[i] = iter.toString();
            }
            return s;
        }
    }

    private function  intoInstruction(obj: RubyArray, n: int, iseq: Instruction): InstructionSequence {
/*         var internalArray: Array = obj.getArray();
        var name: String = internalArray[0].toString();
        var instruction: int = YARVMachine.instruction(name);
        var i: YARVOpCode = new YARVOpCode(instruction);
        if(internalArray.size() > 1) {
            var first:IRubyObject = internalArray[1];
            if(instruction == YARVInstructions.GETLOCAL || instruction == YARVInstructions.SETLOCAL) {
                i.l_op0 = (iseq.locals.length + 1) - RubyNumeric.fix2Number(first);
            } else if(instruction == YARVInstructions.PUTOBJECT || instruction == YARVInstructions.OPT_REGEXPMATCH1 || instruction == YARVInstructions.GETINLINECACHE) {
                i.o_op0 = first;
            } else if(first instanceof RubyString || first instanceof RubySymbol ) {
                i.s_op0 = first.toString();
            } else if(first instanceof RubyNumeric) {
                i.l_op0 = RubyNumeric.fix2Number(first);
            } 
            if(instruction == YARVInstructions.SEND) {
                i.i_op1 = RubyNumeric.fix2int(internalArray[2]);
                i.i_op3 = RubyNumeric.fix2int(internalArray[4]);
            }
            if(instruction == YARVInstructions.DEFINEMETHOD) {
                i.iseq_op = transformIntoSequence(internalArray[2]);
            }
            if(isJump(instruction)) {
                i.index = n;
                jumps.put(i, internalArray[jumpIndex(instruction)].toString());
            }
        } 
        return i; */
        return null;
    }

    private function  isJump(i: int): Boolean {
        return i == YARVInstructions.JUMP || i == YARVInstructions.BRANCHIF || i == YARVInstructions.BRANCHUNLESS || 
            i == YARVInstructions.GETINLINECACHE || i == YARVInstructions.SETINLINECACHE;
    }

    private function  jumpIndex(i: int): int {
        if(i == YARVInstructions.GETINLINECACHE) {
            return 2;
        } else {
            return 1;
        }
    }

    private function  getExceptionInformation(obj: IRubyObject): Array {
        //System.err.println(obj.callMethod(runtime.getCurrentContext(),"inspect"));
        return new Object[0];
    }
}
}